import axios from "axios";
// import io from "socket.io-client";
import { message, notification } from "tdesign-react";
import localforage from "localforage";
import messageStore from "../store/MessageStore";

let useIndexedDb =
  (window.localStorage.getItem("tl-rtc-file-receive-file-use-indexed-db") ||
    "") === "true";

export default class MessageUtil {
  textContent = "";
  roomId = "";
  socketId = "";
  nickName = "";
  recoderId = "";
  rtcConns = {};
  langMode = "zh";
  owner = null;
  config = {}; // rtc配置
  ips = [];
  options = {}; // rtc配置
  wsHost = ""; // ws地址
  useTurn = "true";
  socketHeartbeatFaild = 0;
  openRoomInput = "";
  sendFileRecoderList = [];
  localNetRoomList = [];
  receiveChatRoomList = [];
  sysLogs = [];
  remoteMap = {};
  chooseFileList = [];
  canSaveToIndexedDb = false;
  useIndexedDb = useIndexedDb;

  chooseFileRecoderList = [];

  constructor() {
    return false
    let useTurn =
      (window.localStorage.getItem("tl-rtc-file-use-relay") || "") === "true";
    this.useTurn = useTurn;
    axios
      .get(`http://localhost:9092/api/comm/initData?turn=${useTurn}`)
      .then((initData) => {
        let resData = initData.data;
        this.logo = resData.logo; // 打印logo
        this.version = resData.version; // 项目当前版本
        this.config = resData.rtcConfig; // rtc配置
        this.options = resData.options; // rtc配置
        this.wsHost = resData.wsHost; // ws地址
        console.log("-----------> 建立连接");
        this.socket = window.io(resData.wsHost, {
          transports: ["polling", "websocket"],
          query: {
            token: "123",
            sid: this.socketId,
          },
        });
        this.socketListener();
      });
  }

  addPopup({ title, msg }) {
    notification({ title, content: msg });
  }

  addSysLogs(log) {
    this.sysLogs.push(log);
  }
  // 房间内群聊发言
  sendChatingRoom() {
    this.socket.emit("chatingRoom", {
      content: this.textContent,
      room: this.roomId,
      from: this.socket.id,
      nickName: this.nickName,
      recoderId: this.recoderId,
    });
  }

  //创立链接
  createRtcConnect(id) {
    if (id === undefined) {
      return;
    }

    let that = this;
    let rtcConnect = new RTCPeerConnection(this.config);

    //ice
    rtcConnect.onicecandidate = (e) => {
      that.iceCandidate(rtcConnect, id, e);
    };

    rtcConnect.oniceconnectionstatechange = (e) => {
      that.addSysLogs("iceConnectionState: " + rtcConnect.iceConnectionState);
    };
    //文件发送数据通道
    let sendFileDataChannel = rtcConnect.createDataChannel(
      "sendFileDataChannel"
    );
    sendFileDataChannel.binaryType = "arraybuffer";
    sendFileDataChannel.addEventListener("open", (event) => {
      if (sendFileDataChannel.readyState === "open") {
        that.addSysLogs("建立连接");
      }
    });
    sendFileDataChannel.addEventListener("close", (event) => {
      if (sendFileDataChannel.readyState === "close") {
        that.addSysLogs("关闭连接");
      }
    });
    sendFileDataChannel.addEventListener("error", (error) => {
      console.error(error.error);
      that.addSysLogs("连接断开" + ",file:e=" + error);
      that.removeStream(null, id, null);
    });

    //自定义数据发送通道
    let sendDataChannel = rtcConnect.createDataChannel("sendDataChannel");
    sendDataChannel.binaryType = "arraybuffer";
    sendDataChannel.addEventListener("open", (event) => {
      if (sendDataChannel.readyState === "open") {
        that.addSysLogs("建立连接");
      }
    });
    sendDataChannel.addEventListener("close", (event) => {
      if (sendDataChannel.readyState === "close") {
        that.addSysLogs("关闭连接");
      }
    });
    sendDataChannel.addEventListener("error", (error) => {
      console.error(error.error);
      that.addSysLogs("关闭连接" + ",cus:e=" + error);
      that.removeStream(null, id, null);
    });

    rtcConnect.addEventListener("datachannel", (event) => {
      that.initReceiveDataChannel(event, id);
    });

    rtcConnect.onremovestream = (e) => {
      that.removeStream(rtcConnect, id, e);
    };

    //保存peer连接
    this.rtcConns[id] = rtcConnect;
    if (!this.remoteMap[id]) {
      this.remoteMap[id] = {
        id: id,
        receiveChatRoomSingleList: [],
        p2pMode: "识别中...",
        sendFileDataChannel: sendFileDataChannel,
        sendDataChannel: sendDataChannel,
      };
    }

    return rtcConnect;
  }

  // ice
  iceCandidate(rtcConnect, id, event) {
    if (event.candidate != null) {
      let message = {
        from: this.socketId,
        to: id,
        room: this.roomId,
        sdpMid: event.candidate.sdpMid,
        sdpMLineIndex: event.candidate.sdpMLineIndex,
        sdp: event.candidate.candidate,
      };
      this.socket.emit("candidate", message);
    }
  }

  //移除rtc连接
  removeStream(rtcConnect, id, event) {
    this.getOrCreateRtcConnect(id).close;
    // const remoteInfo = this.getRemoteInfo(id) || {};
    // const removeIsOwner = remoteInfo.owner;

    delete this.rtcConns[id];
    delete this.remoteMap[id];

    //断开连接的时候，剔除此用户的发送记录
    this.sendFileRecoderList = this.sendFileRecoderList.filter((item) => {
      return item.id !== id;
    });
  }

  //获取本地与远程连接
  getOrCreateRtcConnect(id) {
    // 获取rtc缓存连接
    let rtcConnect = this.rtcConns[id];
    // 不存在，创建一个
    if (typeof rtcConnect == "undefined") {
      rtcConnect = this.createRtcConnect(id);
    }
    return rtcConnect;
  }

  //初始化接收数据事件
  initReceiveDataChannel(event, id) {
    if (!id || !event) {
      return;
    }
    let currentRtc = this.getRemoteInfo(id);
    if (!currentRtc) {
      return;
    }

    let receiveChannel = event.channel;

    //文件接收
    if (receiveChannel.label === "sendFileDataChannel") {
      receiveChannel.binaryType = "arraybuffer";
      receiveChannel.onmessage = (evt) => {
        if (this.canSaveToIndexedDb && this.useIndexedDb) {
          this.receiveFileDataToIndexedDb(evt, id);
        } else {
          this.receiveFileDataToMemery(evt, id);
        }
      };
      receiveChannel.onopen = () => {
        const readyState = receiveChannel.readyState;
        this.addSysLogs("文件已经接收" + readyState);
      };
      receiveChannel.onclose = () => {
        const readyState = receiveChannel.readyState;
        this.addSysLogs("文件通道关闭" + readyState);
      };
      this.setRemoteInfo(id, { receiveFileDataChannel: receiveChannel });
    }

    //自定义数据接收
    if (receiveChannel.label === "sendDataChannel") {
      receiveChannel.binaryType = "arraybuffer";
      receiveChannel.onmessage = (evt) => {
        //接收自定义数据 , 暂时用做远程画笔数据接收
        if (!evt || !id) {
          return;
        }
        let data = JSON.parse(evt.data) || {};
        window.Bus.$emit("openRemoteDraw", data);
      };
      receiveChannel.onopen = () => {
        const readyState = receiveChannel.readyState;
        this.addSysLogs("数据接收" + readyState);
      };
      receiveChannel.onclose = () => {
        const readyState = receiveChannel.readyState;
        this.addSysLogs("数据通道关闭" + readyState);
      };
      this.setRemoteInfo(id, { receiveDataChannel: receiveChannel });
    }
  }

  //获取rtc缓存远程连接数据
  getRemoteInfo(id) {
    if (!id) {
      return;
    }
    return this.remoteMap[id];
  }

  //设置rtc缓存远程连接数据
  setRemoteInfo(id, data) {
    if (!id || !data) {
      return;
    }
    let oldData = this.remoteMap[id];
    if (oldData) {
      Object.assign(oldData, data);
      this.remoteMap[id] = oldData;
    }
  }

  // 指定单独发送文件给用户
  sendFileToSingle(recoder) {
    this.chooseFileRecoderList = [recoder];
    this.sendFileRecoderInfo();
  }

  // 多记录并发发送文件基本信息
  sendFileRecoderInfo() {
    // 提前发送文件基础信息
    this.chooseFileRecoderList.forEach((chooseRecoder) => {
      this.socket.emit("message", {
        emitType: "sendFileInfo",
        index: chooseRecoder.index,
        name: chooseRecoder.name,
        type: chooseRecoder.type,
        size: chooseRecoder.size,
        room: this.roomId,
        from: this.socketId,
        nickName: this.nickName,
        to: chooseRecoder.id,
        recoderId: this.recoderId,
      });
    });
  }

  //设置rtc缓存远程连接数据
  setRemoteInfo(id, data) {
    if (!id || !data) {
      return;
    }
    let oldData = this.remoteMap[id];
    if (oldData) {
      Object.assign(oldData, data);
      this.remoteMap[id] = oldData;
    }
  }

  //事件监听
  socketListener() {
    let that = this;

    this.socket.on("localNetRoom", (data) => {
      console.log("局域网房间变动 ------->", data);
      const { mode, list } = data;
      if (mode === "connect") {
        that.localNetRoomList = data.list || [];
        messageStore.localNetRoomList = that.localNetRoomList;
      } else if (mode === "join") {
        let newList = that.localNetRoomList.filter((item) => {
          return item.owner !== list[0].owner;
        });
        newList.push(list[0]);
        messageStore.localNetRoomList.push(list[0]);
        that.localNetRoomList = newList;
      } else if (mode === "exit") {
        let newList = that.localNetRoomList.filter((item) => {
          return item.owner !== list[0].owner;
        });
        if (list[0].count === 0) {
          //退出后房间没人了，清理
          that.localNetRoomList = newList;
        } else {
          // 退出后房间还有人，更新
          newList.push(list[0]);
          that.localNetRoomList = newList;
        }
        messageStore.localNetRoomList = newList;
      }
      if (that.localNetRoomList.length === 0 && that.showLocalNetRoom) {
        that.clickLocalNetRooms(true);
      }
      // messageStore.localNetRoomList = that.localNetRoomList;
    });

    this.socket.on("heartbeat", (data) => {
      if (data.status === "ok") {
        that.addSysLogs("心跳检测成功!" + ": " + data.status);
        //心跳检测失败次数大于0，说明之前是失败的，现在恢复了，刷新页面
        if (that.socketHeartbeatFaild > 0) {
          window.location.reload();
        }
        that.socketHeartbeatFaild = 0;
      } else {
        that.socketHeartbeatFaild += 1;
        that.addSysLogs("websocket心跳检测失败" + ": " + JSON.stringify(data));
      }
    });

    this.socket.on("connect_error", (error) => {
      console.error("connect_error", error);
      if (error) {
        notification.error({
          content: "连接失败" + "[" + that.wsHost + "], " + error.message,
        });
        that.addSysLogs("连接失败" + "[" + that.wsHost + "], " + error.message);
      }
      that.socketHeartbeatFaild += 1;
      that.addSysLogs(
        "websocket心跳检测失败" + ": " + that.socketHeartbeatFaild
      );
    });

    // created作用是让自己去和其他人建立rtc连接
    // 1. 对于screen, video房间来说，是双方都需要传输各自的媒体流
    // 2. 对于live房间来说，只有房主需要获取媒体流
    this.socket.on("created", async function (data) {
      that.addSysLogs("创建房间成功" + JSON.stringify(data));
      that.socketId = data.id;
      that.roomId = data.room;
      that.recoderId = data.recoderId;
      that.owner = data.owner;

      for (let i = 0; i < data.peers.length; i++) {
        let otherSocketId = data.peers[i].id;
        let otherRtcConnect = that.getOrCreateRtcConnect(otherSocketId);
        // 处理完连接后，更新下昵称
        that.setRemoteInfo(otherSocketId, {
          nickName: data.peers[i].nickName,
          langMode: data.peers[i].langMode,
          owner: data.peers[i].owner,
          ua: data.peers[i].ua,
          joinTime: data.peers[i].joinTime,
          userAgent: data.peers[i].userAgent,
          ip: data.peers[i].ip,
          network: data.peers[i].network,
        });

        otherRtcConnect.createOffer(that.options).then(
          (offer) => {
            that.offerSuccess(otherRtcConnect, otherSocketId, offer);
          },
          (error) => {
            that.offerFailed(otherRtcConnect, otherSocketId, error);
          }
        );
      }
    });

    // join的作用是通知其他人，我加入进来了
    this.socket.on("joined", function (data) {
      that.addSysLogs("收到加入房间事件" + JSON.stringify(data));
      that.recoderId = data.recoderId;
      let rtcConnect = that.getOrCreateRtcConnect(data.id);
      // 处理完连接后，更新下昵称
      that.setRemoteInfo(data.id, {
        nickName: data.nickName,
        owner: data.owner,
        langMode: data.langMode,
        ua: data.ua,
        network: data.network,
        joinTime: data.joinTime,
        userAgent: data.userAgent,
        ip: data.ip,
      });
      notification({
        title: "加入房间",
        content: `${data.nickName}加入房间`,
      });
    });

    this.socket.on("offer", function (data) {
      that.addSysLogs("收到offer事件" + JSON.stringify(data));
      let rtcConnect = that.getOrCreateRtcConnect(data.from);
      let rtcDescription = { type: "offer", sdp: data.sdp };
      rtcConnect
        .setRemoteDescription(new RTCSessionDescription(rtcDescription))
        .then((r) => {});
      rtcConnect
        .createAnswer(that.options)
        .then((offer) => {
          that.answerSuccess(rtcConnect, data.from, offer);
        })
        .catch((error) => {
          that.answerFailed(rtcConnect, data.from, error);
        });
    });

    this.socket.on("answer", function (data) {
      that.addSysLogs("收到answer事件" + JSON.stringify(data));
      let rtcConnect = that.getOrCreateRtcConnect(data.from);
      let rtcDescription = { type: "answer", sdp: data.sdp };
      rtcConnect
        .setRemoteDescription(new RTCSessionDescription(rtcDescription))
        .then((r) => {});
    });

    this.socket.on("candidate", function (data) {
      that.addSysLogs("收到candidate事件" + JSON.stringify(data));
      let rtcConnect = that.getOrCreateRtcConnect(data.from);
      let rtcIceCandidate = new RTCIceCandidate({
        candidate: data.sdp,
        sdpMid: data.sdpMid,
        sdpMLineIndex: data.sdpMLineIndex,
      });
      rtcConnect
        .addIceCandidate(rtcIceCandidate)
        .then((res) => {
          that.addIceCandidateSuccess(res);
        })
        .catch((error) => {
          that.addIceCandidateFailed(error);
        });
    });

    this.socket.on("exit", function (data) {
      var rtcConnect = that.rtcConns[data.from];
      if (typeof rtcConnect == "undefined") {
        return;
      } else {
        notification({
          title: "退出房间",
          content: data.from + "退出房间",
        });
        that.addSysLogs("退出房间事件" + JSON.stringify(data));
        that.getOrCreateRtcConnect(data.from).close;
        delete that.rtcConns[data.from];
        delete that.remoteMap[data.from];
      }
    });

    //选中文件时发送给接收方
    this.socket.on("sendFileInfo", function (data) {
      that.addPopup({
        title: "发送文件信息",
        msg: data.from + "已选择文件" + "[ " + data.name + " ], " + "即将发送",
      });
      that.addSysLogs(
        data.from + "已选择文件" + "[ " + data.name + " ], " + "即将发送"
      );

      let fromId = data.from;
      let { receiveFileMap = {} } = that.getRemoteInfo(fromId);
      receiveFileMap[data.index] = Object.assign(
        {
          receivedBuffer: new Array(),
          receivedSize: 0,
        },
        data
      );
      that.setRemoteInfo(fromId, { receiveFileMap });

      // 组装接收文件的recoder结构
      const recoder = {
        id: fromId,
        nickName: data.nickName,
        index: data.index,
        href: "",
        name: data.name,
        type: data.type,
        size: data.size,
        progress: 0,
        done: false,
        start: 0,
        cost: 0,
        upload: "wait",
        indexedDb: that.canSaveToIndexedDb && that.useIndexedDb,
        fragment: 0,
        indexedDbInfoKey: Date.now() + "_" + data.index + "_info",
        indexedDbBufferKey: Date.now() + "_" + data.index + "_buffer",
      };
      that.receiveFileRecoderList.push(recoder);

      that.socket.emit("message", {
        emitType: "sendFileInfoAck",
        room: that.roomId,
        from: that.socketId, // from代表自己发出去的回执
        to: fromId, // 谁发过来的sendFileInfo事件就回执给谁
        index: data.index, //具体的recoder记录文件的索引
      });

      if (that.canSaveToIndexedDb && that.useIndexedDb) {
        that.saveSliceFileInfoToIndexedDb(recoder);
      }
    });

    //接收放已经收到待发送文件信息，代表可以进行发送了，
    //没有ack的话，由于发送文件信息(websocket)和发送文件流(webrtc)是分开的
    //webrtc和websocket之间互存在一个时差，导致接收的时候报错
    this.socket.on("sendFileInfoAck", function (data) {
      let to = data.to;
      let fromId = data.from;
      let index = data.index;
      if (to === that.socketId) {
        // 是自己发出去的文件ack回执
        that.addSysLogs("收到ack回执，准备发送给" + fromId);
        that.setRemoteInfo(fromId, { [index + "ack"]: true });

        //确保所有人都收到基础文件信息，否则，轮训等待
        for (let i = 0; i < that.chooseFileRecoderList.length; i++) {
          let chooseRecoder = that.chooseFileRecoderList[i];
          let remote = that.getRemoteInfo(chooseRecoder.id);
          let ack = remote[chooseRecoder.index + "ack"];
          if (!ack) {
            return;
          }
        }

        //所有人都收到了基础文件信息，开始发送文件
        that.sendFileRecoderData();
      }
    });

    //发送文字内容
    this.socket.on("chatingRoom", function (data) {
      let fromId = data.from;
      that.addPopup({
        title: "发送文字",
        msg: data.from + "发送文字" + "[ " + data.content.substr(0, 10) + " ]",
      });
      that.addSysLogs(
        data.from + "发送文字" + "[ " + data.content.substr(0, 10) + " ]"
      );

      try {
        data.content = tlrtcfile.unescapeStr(data.content);
      } catch (e) {
        that.addSysLogs("文本内容解码失败" + data.content);
      }
      let now = new Date().toLocaleString();

      //私聊
      if (data.to && data.to !== "") {
        //私聊数据放在连接对象中
        let remoteRtc = that.getRemoteInfo(fromId);
        if (remoteRtc) {
          let receiveChatRoomSingleList =
            remoteRtc.receiveChatRoomSingleList || [];
          receiveChatRoomSingleList.push({
            socketId: fromId,
            to: data.to,
            content: data.content,
            nickName: data.nickName,
            time: now,
            timeAgo: window.util ? util.timeAgo(now) : now,
          });
          that.setRemoteInfo(fromId, {
            receiveChatRoomSingleList: receiveChatRoomSingleList,
          });
        }

        that.chatingRoomSingleTpl();
      } else {
        //群聊
        that.receiveChatRoomList.push({
          socketId: fromId,
          content: data.content,
          nickName: data.nickName,
          time: now,
          timeAgo: window.util ? util.timeAgo(now) : now,
        });
      }
    });

    //更新昵称
    this.socket.on("changeNickName", function (data) {
      that.setRemoteInfo(data.from, {
        nickName: data.nickName,
      });
    });

    //提示
    this.socket.on("tips", function (data) {
      message.warning(data.msg);
      if (data.reload) {
        setTimeout(() => {
          window.location.reload();
        }, 1300);
      }
    });
  }

  //文件信息存入indexedDb
  saveSliceFileInfoToIndexedDb(recoder) {
    localforage.setItem(recoder.indexedDbInfoKey, recoder);
  }

  //创建文件发送房间
  createFileRoom() {
    this.openRoomInput = !this.openRoomInput;

    if (this.openRoomInput) {
      return;
    }
    this.roomId = this.roomId.toString().replace(/\s*/g, "");
    if (
      this.roomId === null ||
      this.roomId === undefined ||
      this.roomId === ""
    ) {
      message.warning("请先加入房间");
      return;
    }

    if (this.roomId) {
      this.socket.emit("createAndJoin", {
        room: this.roomId,
        type: "file",
        nickName: this.nickName,
        langMode: this.langMode,
        ua: this.isMobile ? "mobile" : "pc",
        network: this.network,
        localNetRoom: this.useLocalNetworkRoomShare,
        ips: this.ips,
      });
      this.isJoined = true;
      this.addPopup({
        title: "加入房间",
        msg: "加入了房间" + this.roomId,
      });
    }
  }

  // offer
  offerSuccess(rtcConnect, id, offer) {
    rtcConnect.setLocalDescription(offer).then((r) => {});
    let message = {
      from: this.socketId,
      to: id,
      room: this.roomId,
      sdp: offer.sdp,
    };
    this.socket.emit("offer", message);
  }
  // offer
  offerFailed(rtcConnect, id, error) {
    this.addSysLogs(this.lang.offer_failed + error);
  }

  // answer
  answerFailed(rtcConnect, id, error) {
    this.addSysLogs("响应失败" + error);
  }

  //ice
  addIceCandidateFailed(err) {
    this.addSysLogs("冻结失败" + err);
  }
}
